package com.itheima.ws;

import com.alibaba.fastjson.JSON;
import com.itheima.Utils.ResultUtils;
import com.itheima.config.GetHttpSessionConfig;
import com.itheima.ws.pojo.Message;
import com.itheima.ws.pojo.Result;
import com.itheima.ws.pojo.Heart;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;


import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 该类是一个 WebSocket 服务器端点
 * 使用 @ServerEndpoint 注解定义，表示该类是一个 WebSocket 服务器端点
 * value 属性指定该端点的 URL 映射，本例中为 "/chat"
 * onOpen 方法在客户端连接时触发，将客户端添加到连接的客户端列表中
 * onClose 方法在客户端断开连接时触发，将客户端从连接的客户端列表中移除
 * onMessage 方法在客户端发送消息时触发，将消息广播给所有连接的客户端
 * onError 方法在发生错误时触发，打印错误信息
 */

/**
 * 客户端-->服务端
 * // {"toName":"","message":""} 接收到的私聊消息
 * 服务端-->客户端
 * {"is_system":"","fromName":"","message":[name1,name2]}  发送广播消息
 * {"is_system":"","fromName":"","message":""}   发送私聊消息
 */
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
@Slf4j
public class ChatEndPoint {
    /**
     * 线程安全map   多实例共享
     */
    public  static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
    private HttpSession httpSession;
    private String currentUser;

    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //1.保存httpsession
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        if (this.httpSession != null) {
            String user = (String) this.httpSession.getAttribute("user");
            if (user != null) {
                this.currentUser = user;

                onlineUsers.put(user,session);
                //2.向所有登录用户信息广播
                Result result = new Result(true,null,getFriends().toString());
                String message = ResultUtils.toString(result);
                broadcastAllUser(message, null);
            }
        }
        log.info("新用户上线：{}",currentUser);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        if (this.httpSession != null) {
            onlineUsers.remove(currentUser);
            //3.向所有登录用户信息广播
            Result result = new Result(true,null,getFriends().toString());
            String message = ResultUtils.toString(result);
            try {
                broadcastAllUser(message, null);
            } catch (Exception e) {
                log.error("{}异常下线,原因：{}",currentUser,e.getMessage());
            }
        }
        log.info("{}正常下线",currentUser);

    }


    @OnMessage
    public void onMessage(String message) {
//        System.out.println(message);
        Session session = onlineUsers.get(currentUser);
        //接收到心跳消息
        if(message.contains("ping")){
            //心跳消息不做处理
            Heart heart = JSON.parseObject(message, Heart.class);

            System.out.println("收到客户端的心跳" + message.toString());
            //确认心跳
            Heart heart1 = new Heart("pong",heart.getData());
            String message_back = ResultUtils.toString(heart1);
            try {
                sendMessage(session,message_back);
            } catch (IOException e) {
                e.printStackTrace();
            }

            return;
        }
        //1.获取当前登录用户
        if (this.httpSession == null) {
            return;
        }

        //2解析json数据
        Message msg = JSON.parseObject(message, Message.class);
        //判断是否为群聊消息
        if (msg.getToName().toString().equals("all")) {
            Result result = new Result(false,msg.getFromName(), msg.getMessage(),"all");
            String final_msg = ResultUtils.toString(result);
            //广播消息给所有用户
            broadcastAllUser(final_msg,msg.getFromName());
            return;
        }
        //获取接收方session
        Session toUser = onlineUsers.get(msg.getToName());
        //封装消息对象
        Result result = new Result(false, msg.getFromName(), msg.getMessage(),msg.getToName());
        String final_msg = ResultUtils.toString(result);
        System.out.println(msg.getFromName() + "发送给" + msg.getToName());
        //发送消息给接收方
        try {
            sendMessage(toUser,final_msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //获取在线用户集合
    private Set<String> getFriends() {
        return onlineUsers.keySet();
    }
    //广播消息
    private void broadcastAllUser(String message, String excludeName) {
        onlineUsers.entrySet().parallelStream() // 使用并行流提升发送效率(处理大量数据时)
                .filter(entry -> !entry.getKey().equals(excludeName)) // 过滤排除用户
                .forEach(entry -> {
                    Session session = entry.getValue();
                    if (session.isOpen()) { // 检查连接状态
                        try {
                            sendMessage(session,message);
                        } catch (IOException e) {
                            log.error("广播消息失败：{}",entry.getKey(),e);
                            //移除异常会话,程序继续执行
                            onlineUsers.remove(entry.getKey());
                        }
                    } else {
                        onlineUsers.remove(entry.getKey()); // 清理已关闭连接
                    }
                });
    }

    //避免多个线程同时发送消息
    public synchronized void sendMessage(Session session ,String message) throws IOException {
        if (session != null && session.isOpen()) {
            session.getBasicRemote().sendText(message);
        }
    }
}